""" 与用户相关的序列化器 """

from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from sa.models import User
from django.db.models import Q
from django.contrib.auth import authenticate
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.tokens import RefreshToken, AccessToken

"""
因为要使用username 或 password 任一字段实现登录, 不能使用默认的username字段接收数据，
序列化器的父类不能使用 TokenObtainPairSerializer，
因为它会检查 attrs 中是否存在 username字段。
这是因为 TokenObtainPairSerializer 继承自 TokenObtainSerializer，
而 TokenObtainSerializer 的 validate 方法会强制检查 attrs 中是否存在 username 字段。
即使你重写了 validate 方法，父类的逻辑仍然会执行。
"""


# 用户登录序列化器
class UserLoginSerializer(serializers.Serializer):
    account = serializers.CharField(max_length=30)
    password = serializers.CharField(max_length=128, write_only=True)

    # 数据校验
    def validate(self, attrs):
        # 检查是否提供了账号和密码
        account = attrs.get('account')
        if not account:
            raise serializers.ValidationError('请输入用户名或手机号。')
        password = attrs.get('password')
        if not password:
            raise serializers.ValidationError('请输入密码')

        # 查询用户是否存在
        user_obj = User.objects.filter(
            Q(username=account) | Q(mobile=account)).first()
        if not user_obj:
            raise serializers.ValidationError('没有找到此用户。')

        # 校验密码
        # 如果提供的用户名和密码与存储在数据库中的记录匹配，authenticate 将返回一个 User 对象。
        # 如果不匹配，它将返回 None。
        user = authenticate(username=user_obj.username, password=password)
        if not user:
            raise serializers.ValidationError('密码错误。')

        # 手动生成Token
        # rest_framework_simplejwt 提供的方法
        # 有效期较长（通常几天到几周），即使 Access Token 过期，
        # 用户仍然可以通过 Refresh Token 获取新的 Access Token，而无需重新登录。
        refresh = RefreshToken.for_user(user_obj)
        # AccessToken 有效期较短（通常几分钟到几小时），过期后需要重新获取。
        # access = AccessToken.for_user(user_obj)

        token = {
            'refresh': str(refresh),
            'access': str(refresh.access_token),
        }
        # 处理 roles 多对多字段
        # values()：返回字典列表  适用 需要字段名和值（例如 API 返回数据）
        # user_roles = user_obj.roles.all().values('role_name')     # 有键名
        # values_list()：返回元组列表（或扁平化列表）
        # 加上 flat=True 后，values_list() 会直接返回 字段值的列表，去掉元组结构。
        # 适用 只需要值，不需要字段名（例如批量处理数据）。
        user_roles = user_obj.roles.all().values_list('role_name', flat=True)

        # 构建自定义响应数据
        data = {
            'userid': user_obj.id,
            'username': user_obj.username,
            'cn_name': user_obj.cn_name,
            'region': user_obj.region,
            'mobile': user_obj.mobile,
            # 'roles': role_list,
            'roles': user_roles,
            # 'refresh': token['refresh'],
            # 'access': token['access']
            'token': token
        }

        return data


class MyTokenSerializer(TokenObtainPairSerializer):
    # 方法一，重写父类的 `get_token` 方法
    def validate(self, attrs):
        try:
            # super().validate(attrs) 会调用 TokenObtainPairSerializer 的默认验证逻辑，
            # 而默认的验证逻辑是基于 AUTH_USER_MODEL 中定义的用户模型的 username 和 password 字段进行验证的。
            # 调用 super().validate(attrs) 获取默认的 Token 数据（包含 refresh 和 access）的字典。
            original_data = super().validate(attrs)
            print(original_data)
        except Exception as e:
            raise serializers.ValidationError('用户名或密码错误')
        # 增加需要的数据
        data = {
            # 'username': self.user.username,
            'region': self.user.region,
            'mobile': self.user.mobile,
            # 'refresh': old_data['refresh'],
            # 'access': old_data['access']
            'token': original_data  # 双token
        }
        print(data)
        return data

    # 方法二： # 调用父类的 validate 方法
    # def validate(self, attrs):
    #     # 调用父类的 validate 方法生成 Token
    #     original_data = super().validate(attrs)
    #
    #     # 添加自定义字段
    #     data = {
    #        # 'username': self.user.username,
    #         'mobile': self.user.mobile,
    #         'region': self.user.region,
    #         'token': original_data
    #     }
    #     return data


# 用户个人信息序列化器
class UserInfoSerializer(serializers.ModelSerializer):
    password = serializers.CharField(max_length=30, write_only=True,
                                     required=True, help_text='旧密码')
    password1 = serializers.CharField(max_length=30, write_only=True,
                                      required=True, help_text='新密码')
    password2 = serializers.CharField(max_length=30, write_only=True,
                                      required=True, help_text='确认新密码')
    # SlugRelatedField 用于处理外键（ForeignKey） 或 多对多（ManyToMany） 关系，
    # 允许你通过关联模型的 某个特定字段（如 name、slug、email 等） 来表示关系，而不是默认的 ID。
    roles = serializers.SlugRelatedField(many=True, read_only=True, slug_field='role_name')

    class Meta:
        model = User
        fields = ['id', 'username', 'cn_name', 'en_name', 'employee_num',
                  'cse_num', 'region', 'city', 'roles', 'mobile', 'email',
                  'password', 'password1', 'password2']
        read_only_fields = ['id', 'username', 'cn_name', 'en_name', 'employee_num',
                            'email', 'cse_num', 'region', 'city', 'roles', 'mobile']

    # 对单个字段进行额外校验, 验证当前密码是否正确
    def validate_password(self, value):
        """验证当前密码是否正确（用于修改密码时验证旧密码）"""
        # DRF 的序列化器允许你通过 context 参数传递额外的数据, context是个字典
        # DRF 的视图类（如 APIView、GenericAPIView 和视图集 ViewSet）会自动将 request 对象添加到序列化器的上下文中
        # DRF 会自动将 request 对象添加到序列化器的上下文中。
        # 获取当前登录用户
        request = self.context.get('request')
        if not request:
            raise serializers.ValidationError('请求上下文缺失，无法验证密码')
        user = request.user
        if not user or not user.is_authenticated:
            raise serializers.ValidationError('用户未认证, 请先登录')
        if not user.check_password(value):
            raise serializers.ValidationError('当前密码不正确')
        return value

    def validate(self, data):
        """验证 password1 和 password2 是否符合规则"""
        pwd1 = data.get('password1')
        pwd2 = data.get('password2')
        # # 如果两个密码都存在且不匹配
        if pwd1 and pwd2 and pwd1 != pwd2:
            raise serializers.ValidationError({'password2': '密码不一致'})

        # 只验证 password1 和 password2
        password_fields = ['password1', 'password2']
        min_length = 4
        max_length = 12

        for field in password_fields:
            if field in data:  # 确保字段存在
                value = data[field]

                if len(value) < min_length:
                    raise serializers.ValidationError({field: f'密码长度不能小于{min_length}位'})
                if len(value) > max_length:
                    raise serializers.ValidationError({field: f'密码长度不能超过{max_length}位'})
                if value.isdigit():
                    raise serializers.ValidationError({field: '密码不能全是数字'})
                if value.isalpha():
                    raise serializers.ValidationError({field: '密码不能全是字母'})
        return data


# 所有用户的序列化器
class UserAllInfoSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'username', 'cn_name', 'en_name', 'employee_num',
                  'cse_num', 'region', 'city', 'roles', 'mobile', 'email']
